/**
 *
 */
package com.naiterui.ehp.bs.recommend.service.impl;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.itextpdf.text.DocumentException;
import com.naiterui.common.redis.RedisUtil;
import com.naiterui.ehp.bp.bo.consult.VideoConsultBO;
import com.naiterui.ehp.bp.bo.consult.VideoConsultMessageBO;
import com.naiterui.ehp.bp.bo.doctor.RecomDoctorBO;
import com.naiterui.ehp.bp.bo.doctor.RecomUserInfoBO;
import com.naiterui.ehp.bp.bo.emr.DrRecordIdBO;
import com.naiterui.ehp.bp.bo.emr.MedicalRecordBO;
import com.naiterui.ehp.bp.bo.emr.MedicalRecordDoctorBO;
import com.naiterui.ehp.bp.bo.emr.MedicalRecordRecomBO;
import com.naiterui.ehp.bp.bo.emr.params.DrCaseParams;
import com.naiterui.ehp.bp.bo.esign.PreSignResultBO;
import com.naiterui.ehp.bp.bo.esign.RecomSignParamBO;
import com.naiterui.ehp.bp.bo.im.*;
import com.naiterui.ehp.bp.bo.medication.MedicationDefaultUsageBO;
import com.naiterui.ehp.bp.bo.patient.PatientInquirerBO;
import com.naiterui.ehp.bp.constants.CommonConstant;
import com.naiterui.ehp.bp.constants.CommonConstant.ConsultType;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.support.utils.JsonMapper;
import com.naiterui.ehp.bp.utils.biz.AppJumpProtocol;
import com.naiterui.ehp.bp.utils.biz.NameUtils;
import com.naiterui.ehp.bp.utils.biz.ScheduleUtils;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.lang.ConfigUtil;
import com.naiterui.ehp.bp.utils.notice.AppPushUtil;
import com.naiterui.ehp.bp.utils.thread.ThreadPoolUtil;
import com.naiterui.ehp.bs.recommend.bean.*;
import com.naiterui.ehp.bs.recommend.exception.ExceptionCodes;
import com.naiterui.ehp.bs.recommend.func.RecomSettingFunc;
import com.naiterui.ehp.bs.recommend.remote.*;
import com.naiterui.ehp.bs.recommend.repository.mysql.*;
import com.naiterui.ehp.bs.recommend.service.*;
import com.naiterui.ehp.bs.recommend.util.*;
import com.naiterui.ehp.bs.recommend.util.SMSUtil.Template;
import com.naiterui.ehp.bs.recommend.vo.MedicationBoxVO;
import com.naiterui.ehp.bs.recommend.vo.RecomResultVO;
import com.naiterui.ehp.bs.recommend.vo.RecomVO;
import com.naiterui.ehp.bs.recommend.vo.crm.CrmRecomSettingVO;
import com.naiterui.ehp.bs.recommend.vo.dto.IMSendResultDTO;
import com.naiterui.ehp.bs.recommend.vo.dto.RecomInfoDTO;
import com.naiterui.ehp.bs.recommend.vo.esign.ywx.PreSignResultVO;
import com.naiterui.ehp.bs.recommend.vo.param.DiagnosisVO;
import com.naiterui.ehp.bs.recommend.vo.param.RecomItemParamVO;
import com.naiterui.ehp.bs.recommend.vo.param.RecomParamVO;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;

/**
 * 推荐单生成器
 * @author guoyongxiang
 * @since 2.9.0
 */
@Service
@RequiredArgsConstructor
public class RecomCreaterServiceImpl implements IRecomCreaterService {

    private static Logger LOGGER = LoggerFactory.getLogger(RecomCreaterServiceImpl.class);

    /** 推荐缓存有效期24小时 */
    private static final int RECOM_CACHE_EXPIRE = 24 * 60 * 60;

    /** 推药有效期配置 */
    public static final long MEDICATION_RECOM_EXPIRATION = ConfigUtil.getInt("prescription.expire.max") * 60 * 1000L;
    public static final String MEDICATION_RECOM_EXPIRATION_LASTING = ConfigUtil.getString("prescription.expire.lasting");
    private static final String STATIC_BASE_PATH = ConfigUtil.getString("sys.static.resources.base.path");
    private static final String PREC_BASE_PATH_PREFIX = ConfigUtil.getString("tuijian.generate.prescription.path");// ODC处方图片路径前缀

    public static final String HOSPITAL_NAME = ConfigUtil.getString("ehp.hospital.name");

    public static String PUSH_MSG_ASSIGN = "您有新的待审核处方，编号：%s，请及时处理。";
    public static String PUSH_MSG_REASSIGN = "由于您长时间未进行审核，编号：%s，已被重新分配。";

    @Autowired
    private IRecommendRemoter remoter;
    @Autowired
    private RecomSettingFunc recomSettingFunc;
    @Autowired
    private IMedicationRecomRepository medicationRecomRepository;
    @Autowired
    private IMedicationRecomDetailRepository medicationRecomDetailRepository;
    @Autowired
    private IMedicationRecomDiagnosisRepository medicationRecomDiagnosisRepository;
    @Autowired
    private IMedicationRecomInfoRepository medicationRecomInfoRepository;
    @Autowired
    private IRecomService recomService;
    @Autowired
    private IDrDiagnosisService drDiagnosisService;
    @Autowired
    private IDrStandardDiagnosisRepository drStandardDiagnosisRepository;
    @Autowired
    private IRecomAuditLogRepository recomAuditLogRepository;
    @Autowired
    private PrescriptionFileService prescriptionFileService;
    @Autowired
    private IRecommPrescriptionRepository recommPrescriptionRepository;
    @Autowired
    private IEsignFeignClient esignFeignClient;
    @Autowired
    private IRecomEsignPharmacistRepository recomEsignPharmacistRepository;
    @Autowired
    private IRecomAuditService recomAuditService;
    @Autowired
    private IRecomAuditOperationLogService recomAuditOperationLogService;
    @Autowired
    private IPharmacistFeignClient pharmacistFeignClient;
    @Autowired
    private IRecomMessageService recomMessageService;
    @Autowired
    private IMedicationRequireService requireService;
    @Autowired
    private VideoConsultFeignClient videoConsultFeignClient;
    @Autowired
    private IPatientFeignClient patientFeignClient;
    @Autowired
    private IESignService esignService;
    @Autowired
    private ImFeginClient imFeginClient;

    @Value("${ehp.domain.schedule}")
    private String scheduleHost;

    @Value("${ehp.domain.pharmacist}")
    private String pharmacistHost;

    @Autowired
    private IB2cFeignClient b2cFeignClient;
    @Autowired
    private IMedicationRequireRepository medicationRequirerRepository;

    /**
     * @Author guoyongxiang
     * @Date 2017年8月2日
     * @since 1.0.0
     * @param doctorId
     * @param vo
     * @return
     * @throws BusinessException
     * @see IRecomCreaterService#saveRecom(java.lang.Long, RecomParamVO, java.lang.Integer)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public RecomVO saveRecom(Long doctorId, RecomParamVO vo, Integer requestFrom) throws BusinessException {
        //推荐信息校验
        verifyParams(doctorId, vo);
        RecomUserInfoBO userInfo = remoter.getUserInfo(doctorId, vo.getPatientId());
        // 获取医生信息
        RecomDoctorBO doctorInfo = userInfo.getDoctor();

        //验证处方中是否含有非标准诊断
        verifyRecomDiagnosis(doctorInfo, vo);

        // 医嘱处方医生标识
        Boolean recordRecom = doctorInfo.getRecordRecom();

        DrCaseParams params = vo.getCaseParams();

        // 校验病历
        if (params != null && !recordRecom) {
            LOGGER.info("病历参数不为空，但不是医嘱处方医生");
            throw new BusinessException(ExceptionCodes.NOT_RECORD_RECOM);
        }
        // 装配病历数据
        if (params != null) {
            recomToDrCaseParams(doctorId, vo, params);
        }
        // 推药条件校验
        verifyRecomLimit(doctorInfo, vo.getInquirerId());

        long start = System.currentTimeMillis();
        // 回写推药患者备注名(判断是医嘱处方医生,有病历，在病历接口中判断是否反写)
        boolean changedRemarkName = false;
        recomService.saveRemarkName(doctorId, vo.getPatientId(), vo.getPatientName(), vo.getPatientGender(), vo.getPatientAge(),
                vo.getPatientAgeUnit());
        LOGGER.info("[recom cost]更新患者备注名 , cost:{}", System.currentTimeMillis() - start);

        //增加固化商品价格和商品名称
        setSalePirce(doctorId, doctorInfo.getPriceCityId(), vo);

        // 保存推荐单记录
        MedicationRecom recom = saveRecomHandle(doctorId, vo, doctorInfo.getDepartmentId());

        // 保存处方信息
        RecomInfoDTO recomInfoDTO = saveRecomInfoHandle(recom.getId(), vo, userInfo, recom.getDiagnosis());
        // 保存处方文件&预签署
        List<PreSignResultVO> preSignResults = savePrescriptionFile(userInfo.getDoctor().getHospitalName(), recom, recomInfoDTO.getRecomInfo(),
            vo.getItems(), requestFrom);

        List<String> eSignSerial = null;
        String faceUrl = null;
        if (CollectionUtils.isNotEmpty(preSignResults)) {
            eSignSerial = preSignResults.stream().map(PreSignResultVO::getUniqueId).collect(Collectors.toList());
            faceUrl = preSignResults.get(0).getFaceUrl();
        }

        // 推荐id放到病历中,并保存病历信息为暂存
        String recordId = null;
        if (params != null) {
            params.setRecommendId(recom.getId());
            try {
                DrRecordIdBO recordIdBO = remoter.saveDrCase(params);
                recordId = recordIdBO.getRecordId();
            } catch (Exception e) {
                LOGGER.warn("保存病历错误");
                throw new BusinessException(ExceptionCodes.FAILED);
            }
        }

        return RecomVO.builder().recomId(recom.getId())
                .changedRemarkName(changedRemarkName)
                .recordId(recordId)
                .eSignSerial(eSignSerial)
                .faceUrl(faceUrl)
                .build();
    }


    /**
     * 固化商品价格和商品名称——历史实现，新实现见syncProductInfo()
     * RecomCreaterServiceImpl.setSalePirce()
     * @Author gongbaoqiang
     * @Date 2018年8月27日
     * @since 3.1.0
     * @param vo
     */
    private void setSalePirce(Long doctorId, Integer cityId, RecomParamVO vo)
            throws BusinessException {
        LOGGER.info("开始药品价格固化处理");
        List<RecomItemParamVO> items = vo.getItems();
        List<Long> skuIds = new ArrayList<>();
        items.stream().forEach(s -> {
            skuIds.add(s.getSkuId());
        });

        List<MedicationBoxVO> medicaList;
        //获取药品详情
        try {
            medicaList = remoter.getMedicationList(doctorId, cityId, skuIds, false);
        } catch (Exception e) {
            LOGGER.error("药品价格固化---- 获取药品明细异常：skuIds=【{}】", skuIds.toString());
            throw new BusinessException(ExceptionCodes.HTTP_ERR);
        }
        Map<Long, MedicationBoxVO> map = medicaList.stream().collect(Collectors.toMap(MedicationBoxVO::getSkuId, s -> s));
        //设置药品价格到推荐详情
        items.stream().forEach(s -> {
            MedicationBoxVO boxVO = map.get(s.getSkuId());
            if (boxVO != null) {
                s.setSalePrice(boxVO.getSalePrice());
                s.setProductName(boxVO.getName());
                s.setCommonName(boxVO.getCommonName());
                s.setRecomName(boxVO.getRecomName());
            }
        });
        vo.setItems(items);
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public RecomResultVO saveRecomConfirm(Long doctorId, Long recomId, Boolean force, Integer consultType, Long consultId, String recordId, String uniqueId) throws BusinessException {
        LOGGER.info("开始发送处方, doctorId: {}, recomId: {}, force: {}, consultType: {}, consultId: {}, recordId: {}, uniqueId: {}",
                doctorId, recomId, force, consultType, consultId, recordId, uniqueId);
        long start = System.currentTimeMillis();
        // 查询推荐数据
        MedicationRecom recom = medicationRecomRepository.get(recomId);
        if (recom == null) {
            LOGGER.warn("[recom]推荐不存在recomId:{}, doctorId:{}", recomId, doctorId);
            throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_EXITS);
        }

        // 查询推荐药品详情列表
        List<MedicationRecomDetail> detail = medicationRecomDetailRepository.findByRecommendId(recomId);

        // 查询推荐处方信息
        DrMedicationRecomInfo recomInfo = medicationRecomInfoRepository.findFirstByRecommendId(recomId);

        LOGGER.info("[recom cost]推荐表信息查询 recomId:{}, cost:{}", recomId, System.currentTimeMillis() - start);

        start = System.currentTimeMillis();
        // 获取医生患者信息(手机)
        RecomUserInfoBO userInfo = remoter.getUserInfo(doctorId, recom.getPatientId());
        LOGGER.info("[recom cost]查询医生信息 recomId:{}, cost:{}", recomId, System.currentTimeMillis() - start);

        // 业务逻辑状态校验
        verifyRecomConfirmInfo(recomId, doctorId, recom.getPatientId(), recom.getForceRecom(), force, recomInfo, userInfo);

        recom.setForceRecom(MedicationRecom.FORCE_RECOM_STATUS_SEND);

        if (force != null && force) {
            // 不电子签名-需要签名医生-签名失败，强制推荐
            medicationRecomInfoRepository.updateDoctorSignStatus(DrMedicationRecomInfo.DOCTOR_SIGN_STATUS_FAIL, recom.getId());

            recom.setForceRecom(MedicationRecom.FORCE_RECOM_STATUS_FORCE_SEND);
        }

        recom.setType(ConsultType.VIDEO == consultType && consultId != null && consultId != 0
            ? MedicationRecom.RECOM_TYPE_VIDEO : recom.getType());

        //返回vo
        RecomResultVO recomRecordVO = new RecomResultVO();
        //图文问诊，一键续方的处方不开启会话
        // if (recom.getRequireId() == null || recom.getRequireId() == 0L) {
        start = System.currentTimeMillis();
        // 向IM发送推药消息
        IMSendResultDTO recomSendDTO = sendRecomMessage(recom, detail, recomInfo, recordId, consultType);
        LOGGER.info("[recom cost]发送IM消息 recomId:{}, cost:{}", recomId, System.currentTimeMillis() - start);
        LOGGER.info("发送推药结果：{}", JSONUtil.toJsonStr(recomSendDTO));
        if (null != recomSendDTO.getConsultSessionId()) {
            // 更新会话表推药标识
            remoter.updateConsultSessionRecomFlag(recomSendDTO.getConsultSessionId());
            recom.setChangedAt(new Date());
            recom.setSessionId(recomSendDTO.getConsultSessionId());

            // 获取会话详情，设置病情病例信息
            List<SessionDetailBO> sessionDetails =
                    imFeginClient.sessionDetailList(recomSendDTO.getConsultSessionId().toString());
            if (CollectionUtils.isNotEmpty(sessionDetails) && sessionDetails.get(0).getSpecificMessageType() != null) {
                SessionDetailBO sessionDetailBO = sessionDetails.get(0);
                if (sessionDetailBO
                        .getSpecificMessageType() == SessionDetailBO.SPEC_MESS_TYPE_PATIENTS_MEDICAL_RECORD) {
                    recomInfo.setCaseId(sessionDetailBO.getRecordId());
                } else if (sessionDetailBO
                        .getSpecificMessageType() == SessionDetailBO.SPEC_MESS_TYPE_PATIENTS_DISEASE_RECORD) {
                    recomInfo.setDiseaseId(sessionDetailBO.getRecordId());
                }
                medicationRecomInfoRepository.update(recomInfo);
            }
            medicationRecomRepository.update(recom);
        }
        // 续方的不返回session信息
        if (recom.getRequireId() == null || recom.getRequireId() == 0L) {
            recomRecordVO =
                new RecomResultVO(recomSendDTO.getSessionId(), recomSendDTO.getMessageId(), recomSendDTO.getSendTime(),
                    recomSendDTO.getBeginTime(), recomInfo.getCheckingStatus(), recomSendDTO.getNewSessionFlag(),
                    recomSendDTO.getConsultPayType(),
                    recomSendDTO.getConsultSourceType());
        }
//        } else {
//            //如果系统审核，直接推送消息到患者
//            if (DrMedicationRecomInfo.CHECKING_STATUS_DEF == recomInfo.getCheckingStatus()) {
//                recomMessageService.sendPtMessage(doctorId, recomInfo.getPatientId(), recomId, detail);
//            }
        //续方求药
        if (recom.getRequireId() != null && recom.getRequireId() != 0L) {
            try {
                MedicationRequire medicationRequire = medicationRequirerRepository.get(recom.getRequireId());
                DrMedicationRecomInfo medicationRecomInfo =
                    medicationRecomInfoRepository.findFirstByRecommendId(medicationRequire.getFormerRecommendId());
                recomInfo.setDiseaseId(medicationRecomInfo.getDiseaseId());
                recomInfo.setCaseId(medicationRecomInfo.getCaseId());
                MedicationRecom medicationRecom =
                    medicationRecomRepository.get(medicationRequire.getFormerRecommendId());
                recom.setVideoConsultId(medicationRecom.getVideoConsultId());

                //更新求药申请状态为已续方
                requireService.handleRequire(recom.getRequireId(), MedicationRequire.APPLY_STATUS_REPEAT, null);
            } catch (BusinessException e) {
                LOGGER.error("更新续方申请状态异常：msg={}", e.getMessage());
            }
        }
        //视频问诊
        if (ConsultType.VIDEO == consultType && consultId != null && consultId != 0) {

            try {
                recom.setVideoConsultId(consultId);
                // 查询视频问诊详情，设置病情病例信息
                VideoConsultBO videoConsultBO = videoConsultFeignClient.getVideoConsult(consultId);
                recomInfo.setCaseId(videoConsultBO.getCaseId());
                recomInfo.setDiseaseId(videoConsultBO.getDiseaseId());
                //保存视频问诊处方记录
                videoConsultFeignClient.saveVideoConsultMessage(VideoConsultMessageBO.builder()
                        .videoConsultId(consultId)
                        .doctorId(recom.getDoctorId())
                        .patientId(recom.getPatientId())
                        .inquirerId(recom.getInquirerId())
                        .type(VideoConsultMessageBO.VIDEO_CONSULT_TYPE_TO_RECOM)
                        .content(recom.getId().toString())
                        .build());
            } catch (Exception e) {
                LOGGER.error("保存视频问诊处方记录：ERROR ;", e);
            }
            medicationRecomRepository.update(recom);
            medicationRecomInfoRepository.update(recomInfo);

        }
//        }

        start = System.currentTimeMillis();
        // 重置推药提醒
        rePurchaseRemindReset(recom.getPatientId());
        LOGGER.info("[recom cost]重置购药提醒 recomId:{}, cost:{}", recomId, System.currentTimeMillis() - start);
        // 获取诊断信息
        List<DrMedicationRecomDiagnosis> diagnosisByRecomId = medicationRecomDiagnosisRepository.findByRecommendId(recomId);
        List<String> diagnosis = diagnosisByRecomId.stream().map(DrMedicationRecomDiagnosis::getName).collect(Collectors.toList());
        PatientIntelligentLabelUtil.putDiagnosis(doctorId, recom.getInquirerId(), diagnosis);

        // 保存操作日志
        recomAuditOperationLogService.addLog(recom.getId(), recomInfo.getDoctorName(), RecomAuditOperationLog.TYPE_SEND);

        // 向患者发送医生开发系统提醒
        syncRecomAudit(recom, recomInfo);
        // 向患者发送医生开发系统提醒
        PatientMsgSendParamBO patientMsgSendParamBO = new PatientMsgSendParamBO();
        patientMsgSendParamBO.setMsgType(Constants.MESSAGE_TYPE_PATIENTS_PRESCRIPTION);
        patientMsgSendParamBO.setDoctorId(doctorId);
        patientMsgSendParamBO.setPatientId(recomInfo.getPatientId());
        Long inquirerId = recomInfo.getInquirerId();
        patientMsgSendParamBO.setInquirerId(inquirerId);
        patientMsgSendParamBO.setContent(ConfigUtil.getString("recom.message.text"));
        imFeginClient.sendPatientSysMessage(patientMsgSendParamBO);
        LOGGER.info("医生发送处方系统提醒：{}", JSONUtil.toJsonStr(patientMsgSendParamBO));

        // 如果处方只有一个药品
        if (detail.size() == 1) {
            List<PatientInquirerBO> inquirerBOList = patientFeignClient.getPatientInquirer(Collections.singletonList(inquirerId));
            if (CollectionUtils.isEmpty(inquirerBOList)) {
                LOGGER.info("系统自动审核处方--没有就诊人， recomId: {}, inquirerId: {}", recomId, inquirerId);
                return recomRecordVO;
            } else {
                PatientInquirerBO inquirerBO = inquirerBOList.get(0);
                Integer ageYear = inquirerBO.getAgeYear();
                if (null == ageYear || 15 > ageYear) {
                    LOGGER.info("系统自动审核处方--儿童就诊人不自动审核， recomId: {}, inquirerId: {}, ageYear: {}", recomId, inquirerId,
                        ageYear);
                    return recomRecordVO;
                }
            }
            MedicationRecomDetail recomDetail = detail.get(0);
            Set<String> pharmacistIdSet = RedisUtil.setOps().smembers(CommonConstant.AUTO_AUDIT_PHARMACIST_KEY);
            Long skuId = recomDetail.getSkuId();

            if (CollUtil.isEmpty(pharmacistIdSet)) {
                LOGGER.info("系统自动审核处方--没有设置审核药师， recomId: {}, skuId: {}", recomId, skuId);
                return recomRecordVO;
            }

            // 获取该处方药品的用法用量
            Map<Long, MedicationDefaultUsageBO> usage = b2cFeignClient.getMedicationDefaultUsage(Collections.singleton(skuId));
            if (CollUtil.isEmpty(usage)) {
                LOGGER.info("系统自动审核处方--该药品没有设置用法用量， recomId: {}, skuId: {}", recomId, skuId);
                return recomRecordVO;
            }
            MedicationDefaultUsageBO defaultUsageBO = usage.get(skuId);

            // 验证剂量单位
            String usageBOEachDoseUnit = defaultUsageBO.getEachDoseUnit();
            if (StrUtil.isBlank(usageBOEachDoseUnit)) {
                LOGGER.info("系统自动审核处方--未配置剂量单位， recomId: {}, skuId: {}, usageBOEachDoseUnit: {}", recomId, skuId,
                    usageBOEachDoseUnit);
                return recomRecordVO;
            }
            String eachDoseUnit = recomDetail.getEachDoseUnit();
            if (!eachDoseUnit.equals(usageBOEachDoseUnit)) {
                LOGGER.info("系统自动审核处方--剂量单位和安全用量不一致， recomId: {}, skuId: {}, eachDoseUnit: {}, usageBOEachDoseUnit: {}",
                    recomId, skuId, eachDoseUnit, usageBOEachDoseUnit);
                return recomRecordVO;
            }
            String medicineMinUnit = defaultUsageBO.getMedicineMinUnit();
            if (StrUtil.isBlank(medicineMinUnit)) {
                LOGGER.info("系统自动审核处方--没有药品最小单位， recomId: {}, skuId: {}, medicineMinUnit: {}", recomId, skuId,
                    medicineMinUnit);
                return recomRecordVO;
            }
            if (!eachDoseUnit.equals(medicineMinUnit)) {
                LOGGER.info("系统自动审核处方--剂量单位和药品最小单位不一致， recomId: {}, skuId: {}, eachDoseUnit: {}, medicineMinUnit: {}",
                    recomId, skuId, eachDoseUnit, medicineMinUnit);
                return recomRecordVO;
            }
            // 验证剂量
            Double doseMax = defaultUsageBO.getDoseMax();
            Double doseMin = defaultUsageBO.getDoseMin();
            if (doseMax == null || doseMin == null) {
                LOGGER.info("系统自动审核处方--未配置安全剂量范围， recomId: {}, skuId: {}, doseMax: {}, doseMin: {}", recomId, skuId,
                    doseMax, doseMin);
                return recomRecordVO;
            }
            double eachDosageCount = Double.parseDouble(recomDetail.getEachDosageCount());
            if (!(doseMax >= eachDosageCount && eachDosageCount >= doseMin)) {
               LOGGER.info("系统自动审核处方--剂量和安全用量不一致， recomId: {}, skuId: {}, doseMax: {}, eachDosageCount: {}, doseMin: {}", recomId, skuId, doseMax, eachDosageCount, doseMin);
                return recomRecordVO;
            }

            // 验证用药周期
            Integer medicationCycleMax = defaultUsageBO.getMedicationCycleMax();
            Double drugCycle = recomDetail.getDrugCycle();
            String drugCycleUnit = recomDetail.getDrugCycleUnit();
            if (medicationCycleMax == null || drugCycle == null || StrUtil.isBlank(drugCycleUnit)) {
                LOGGER.info(
                    "系统自动审核处方--没有用药周期， recomId: {}, skuId: {}, medicationCycleMax: {},drugCycle:{}, drugCycleUnit: {}",
                    recomId, skuId, medicationCycleMax, drugCycle, drugCycleUnit);
                return recomRecordVO;
            }
            double computeDays = computeDays(drugCycle, drugCycleUnit);
            if (computeDays > medicationCycleMax) {
                LOGGER.info("系统自动审核处方--用药周期大于安全用量， recomId: {}, skuId: {}, computeDays: {}, medicationCycleMax: {}",
                    recomId, skuId, computeDays, medicationCycleMax);
                return recomRecordVO;
            }

            // 验证用药频次
            Integer frequencyMax = defaultUsageBO.getFrequencyMax();
            Integer frequencyMin = defaultUsageBO.getFrequencyMin();
            if (frequencyMax == null || frequencyMin == null) {
                LOGGER.info("系统自动审核处方--未配置用药频次范围， recomId: {}, skuId: {}, frequencyMax: {}, frequencyMin: {}", recomId,
                    skuId, frequencyMax, frequencyMin);
                return recomRecordVO;
            }
            double dosageCount = Double.parseDouble(recomDetail.getDosageCount());
            if (!(frequencyMax >= dosageCount && dosageCount >= frequencyMin)) {
                LOGGER.info("系统自动审核处方--用药频次和安全用量不一致， recomId: {}, skuId: {}, frequencyMax: {}, dosageCount:{}, frequencyMin: {}", recomId, skuId, frequencyMax, dosageCount, frequencyMin);
                return recomRecordVO;
            }

            // 验证客户端计算的盒数是否和服务端一致
            // 药品最小规格包装书
            Integer packagSpec = defaultUsageBO.getPackagSpec();
            if (packagSpec == null) {
                LOGGER.info("系统自动审核处方--未配置最小规格数量， recomId: {}, skuId: {}, packagSpec: {}", recomId, skuId, packagSpec);
                return recomRecordVO;
            }
            // 服务端计算的盒数
            Double dosageCycle = recomDetail.getDosageCycle();
            String dosageCycleUnit = recomDetail.getDosageCycleUnit();
            if (dosageCycle == null || StrUtil.isBlank(dosageCycleUnit)) {
                LOGGER.info("系统自动审核处方--没有用量周期， recomId: {}, skuId: {}, dosageCycle: {}, dosageCycleUnit: {}", recomId,
                    skuId, dosageCycle, dosageCycleUnit);
                return recomRecordVO;
            }
            int squantity =
                calculationQuantity(drugCycle, dosageCycle, dosageCycleUnit, packagSpec, dosageCount, eachDosageCount);
            // 客户端计算的盒数
            Integer quantity = recomDetail.getQuantity();
            if (squantity != quantity) {
                LOGGER.info(
                    "系统自动审核处方--客户端计算的盒数和服务端计算的盒数不一致， recomId: {}, skuId: {}, squantity: {}, quantity:{}, packagSpec: {}, dosageCount: {}, eachDosageCount: {}",
                    recomId, skuId, squantity, quantity, packagSpec, dosageCount, eachDosageCount);
                return recomRecordVO;
            }

            // 存储处方不自动分配标识
            int delayTime = ConfigUtil.getInt("auto.audit.delay.time");
            // 占用30秒
            RedisUtil.valueOps().set(CommonConstant.NOT_AUTO_ASSIGN_RECOM + recomId, recomId, 30);

            // 某个分钟后
            String invokeTime =
                DateUtil.formatDate(DateUtil.addSeconds(new Date(), delayTime), DateUtil.CRON_TIME_FORMAT);
            LOGGER.info("处方开始定时自动审核， recomId: {}， invokeTime: {}", recomId, invokeTime);
            Map<String, String> paramMap = Maps.newHashMap();
            paramMap.put("recomId", String.valueOf(recomId));
            ScheduleUtils.addCustomerTask(this.scheduleHost, invokeTime, ScheduleUtils.TASH_TYPE_AUTO_AUDIT, String.valueOf(recomId),
                    this.pharmacistHost + "/inner/recommend/auto/audit", paramMap);
        }
        return recomRecordVO;
    }

    /**
     * 保存推荐单主记录
     * RecomServiceImpl.saveRecom()
     * @Author guoyongxiang
     * @Date 2017年8月1日
     * @since 2.9.0
     * @param doctorId
     * @param vo
     * @param departmentId
     * @return
     * @throws BusinessException
     */
    private MedicationRecom saveRecomHandle(Long doctorId, RecomParamVO vo, Integer departmentId) throws BusinessException {

        // ***********************创建推荐主单记录***********************
        // 校验推荐单外部ext_id是否重复
        MedicationRecom recom = medicationRecomRepository.findFirstByDoctorIdAndPatientIdAndExtId(doctorId, vo.getPatientId(),
                vo.getTimestamp() + "");
        if (recom != null) {
            LOGGER.warn("[recom]推荐单外部推荐号已存在,extId=" + vo.getTimestamp() + ",doctorId=" + doctorId + ",patientId=" + vo.getPatientId() + ",extId="
                    + vo.getTimestamp());
            throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_ALREADY_EXISTS);
        } else {
            String diagnosis = getDiagnosisStr(vo.getDiagnosis());
//            recom = new MedicationRecom(doctorId, vo.getPatientId(), vo.getTimestamp() + "", MEDICATION_RECOM_EXPIRATION,
            recom = new MedicationRecom(doctorId, vo.getPatientId(), vo.getTimestamp() + "", DateUtil.parseStrToDateTime(MEDICATION_RECOM_EXPIRATION_LASTING),
                    MedicationRecom.RECOM_TYPE_MANNUL, vo.getRequireId(), MedicationRecom.FORCE_RECOM_STATUS_NOT_SEND, diagnosis);
            recom.setInquirerId(vo.getInquirerId());
        }

        // 设置推荐单编号
        recom.setSerialNumber(SerialNumberUtil.genRecomNumber(departmentId));

        // 设置推荐类型
        if (vo.getRequireId() > 0L) {
            recom.setType(MedicationRecom.RECOM_TYPE_REQUIRE);
        }
        //设置病历数据收集状态
        recom.setCollectStatus(MedicationRecom.COLLECT_STATUS_AGREE);

        //增加推荐来源记录医生端
        recom.setOriginRecom(vo.getOriginRecom());
        recom.setStatus(CommonConstant.DOCTOR_RECOM_STATUS_NOT_BUY);
        recom.setVisiable(Constants.STATUS_NO.byteValue());
        //设置安全用药发送标记医生端
        recom.setSafeStatus(MedicationRecom.SAFE_STATUS_DEFAULT);
        recom.setInvalid(MedicationRecom.INVALID_DEFAULT);
        medicationRecomRepository.save(recom);

        // ***********************创建推荐单详情***********************
        for (RecomItemParamVO item : vo.getItems()) {
            MedicationRecomDetail detail = new MedicationRecomDetail(recom.getId(), item.getProductId(), item.getSkuId(), item.getProductName(),
                    item.getCommonName(), item.getRecomName(), item.getQuantity(), item.getUsage(),
                    item.getBackup(), item.getDosageCycle(), item
                    .getDosageCycleUnit(),
                    item.getDosageCount(), item.getEachDosageCount(), item.getEachDoseUnit(),
                    item.getDrugCycle(), item.getDrugCycleUnit(),
                    item.getQuantityUnit(), item.getUsageMethod(), item.getUsageTime());
            detail.setSalePrice(new BigDecimal(item.getSalePrice()).intValue());
            medicationRecomDetailRepository.save(detail);
        }

        // ***********************保存诊断记录***********************
        long start = System.currentTimeMillis();

        List<DiagnosisVO> diagnosisList = drDiagnosisService.updateCommonDiagnosis(doctorId, vo.getDiagnosis());
        for (DiagnosisVO diagnosis : diagnosisList) {
            medicationRecomDiagnosisRepository
                    .save(new DrMedicationRecomDiagnosis(recom.getId(), diagnosis.getId(), diagnosis.getName(), diagnosis.getType()));
        }
        LOGGER.info("[recom cost]保存诊断关系 recomId:{}, cost:{}", recom.getId(), System.currentTimeMillis() - start);

        return recom;
    }

    /**
     * 保存处方信息
     * RecomServiceImpl.saveRecomInfoHandle()
     * @Author guoyongxiang
     * @Date 2017年8月1日
     * @since 2.9.0
     * @param recomId
     * @param vo
     * @param userInfo
     * @param diagnosis
     * @return
     * @throws BusinessException
     */
    private RecomInfoDTO saveRecomInfoHandle(Long recomId, RecomParamVO vo, RecomUserInfoBO userInfo, String diagnosis) throws BusinessException {

        DrMedicationRecomInfo recomInfo = new DrMedicationRecomInfo();
        recomInfo.setRecommendId(recomId);
        recomInfo.setDoctorId(userInfo.getDoctor().getId());
        recomInfo.setPatientId(userInfo.getPatient().getId());
        recomInfo.setInquirerId(vo.getInquirerId());
        recomInfo.setRelation(vo.getRelation());
        recomInfo.setDoctorName(userInfo.getDoctor().getName());

        recomInfo.setHospitalName(userInfo.getDoctor().getHospitalName());
        recomInfo.setRecordHospitalId(userInfo.getDoctor().getRecordHospitalId());

        recomInfo.setPatientName(vo.getPatientName());
        recomInfo.setPatientGender(vo.getPatientGender());
        recomInfo.setPatientAge(vo.getPatientAge());
        recomInfo.setPatientAgeStr(vo.getPatientAgeStr());
        recomInfo.setPatientAgeUnit(vo.getPatientAgeUnit());


        // 根据阳光化标识设置患者端显示的诊金
        int showPublicFee = DrMedicationRecomInfo.PUBLIC_FEE_NOT_DISPLAY;
        int consultFee = 0;
        int visitFee = 0;
        if (userInfo.getDoctor().getPublic()) {
            showPublicFee = DrMedicationRecomInfo.PUBLIC_FEE_DISPLAY;
            int pointToRMBScale = ConfigUtil.getInt("sysArg.pointToRMBScale"); // 积分兑换比例
        }
        recomInfo.setShowPublicFee(showPublicFee);
        recomInfo.setConsultFee(consultFee);
        recomInfo.setVisitFee(visitFee);

        Date date = new Date();
        recomInfo.setCreatedAt(date);
        recomInfo.setCreatedBy("recomSystem");
        recomInfo.setChangedAt(date);
        recomInfo.setChangedBy("recomSystem");

        //获取该医生的一级科室（自定义除外）
        String departmentName = getDepartmentName(userInfo);
        recomInfo.setDepartmentName(departmentName == null ? "" : departmentName);
        //设置科室和医院信息，生成处方图片使用
        userInfo.getDoctor().setDepartmentName(departmentName);
        userInfo.getDoctor().setHospitalName(HOSPITAL_NAME);

        //药师签名状态不能为空
        recomInfo.setPharmacistSignStatus(DrMedicationRecomInfo.PHARMACIST_SIGN_STATUS_UNDO);
        // 未备案或ODC处方图生成失败(处方单所有商品均为非药品)
        recomInfo.setDoctorSignStatus(DrMedicationRecomInfo.DOCTOR_SIGN_STATUS_UNDO);
        // 调配药师签名状态
        recomInfo.setAllocatePharmacistSignStatus(DrMedicationRecomInfo.AL_PHARMACIST_SIGN_STATUS_UNDO);

        // 获取CRM审核开关设置
        settingCrmConfig(recomInfo, userInfo.getDoctor().getRecordStatus());

        // 自动审核,医生需要签名
        if (recomInfo.getDoctorSignTime() != null && recomInfo.getCheckingDelay() > 0
                && DrMedicationRecomInfo.PHARMACIST_SIGN_CONFIG_ON == recomInfo.getPharmacistSignConfig()
                && DrMedicationRecomInfo.CHECKING_STATUS_IN == recomInfo.getCheckingStatus()
                && DrMedicationRecomInfo.AUDIT_TYPE_AUTO == recomInfo.getCheckingConfig()) {
            // 设置处方药师审核时间
            recomInfo.setPharmacistSignTime(DateUtils.addSeconds(recomInfo.getDoctorSignTime(), recomInfo.getCheckingDelay()));
        }

        // 不审核，医生需签名
        if (recomInfo.getDoctorSignTime() != null && recomInfo.getCheckingDelay() <= 0
                && DrMedicationRecomInfo.PHARMACIST_SIGN_CONFIG_ON == recomInfo.getPharmacistSignConfig()
                && DrMedicationRecomInfo.CHECKING_STATUS_DEF == recomInfo.getCheckingStatus()
                && DrMedicationRecomInfo.AUDIT_TYPE_AUTO == recomInfo.getCheckingConfig()) {
            // 设置处方药师审核时间
            recomInfo.setPharmacistSignTime(DateUtils.addSeconds(recomInfo.getDoctorSignTime(), DrMedicationRecomInfo.DEFAULT_PHARMACIST_SIGN_DELAY));
            recomInfo.setPharmacistSignStatus(DrMedicationRecomInfo.PHARMACIST_SIGN_STATUS_UNDO);
        }
        medicationRecomInfoRepository.save(recomInfo);

        return RecomInfoDTO.builder().recomInfo(recomInfo).build();
    }

    private void verifyParams(Long doctorId, RecomParamVO vo) throws BusinessException {
        if (vo == null) {
            LOGGER.warn("[recom]推荐单参数对象为空");
            throw new BusinessException(ExceptionCodes.PARAM_ERROR);
        } else if (doctorId == null || vo.getPatientId() == null || vo.getTimestamp() == null || vo.getType() == null || vo.getDiagnosis() == null
                || vo.getDiagnosis().size() < 1) {
            LOGGER.warn("[recom]推荐单必要参数为空doctorId={}, patientId={}, extId={}, type:{}", doctorId, vo.getPatientId(), vo.getTimestamp(), vo.getType());
            throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_MUST_PARAMS_NULL);
        } else {
            if (vo.getItems() == null || vo.getItems().size() < 1) {
                LOGGER.warn("[recom]推荐单商品列表为空");
                throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_ITEMS_NULL);
            }
            for (RecomItemParamVO item : vo.getItems()) {
                if (item.getProductId() == null || item.getSkuId() == null || item.getQuantity() < 1) {
                    LOGGER.warn("[recom]推荐单商品信息缺失或不合法,pid:{}, skuId:{}, quantity:{}, productName:{}", item.getProductId(), item.getSkuId(),
                            item.getQuantity(), item.getProductName());
                    throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_ITEMS_VALID);
                }

                // 如果是新版本，需要校验用法用量详情
                if (item.isContainUsageDetail() && (item.getDosageCycle() == null || StringUtils.isEmpty(item.getDosageCount())
                        || StringUtils.isEmpty(item.getDosageCycleUnit()))) {
                    // 新版用法用量校验.每(几)(日)(几)次必填
                    LOGGER.warn("[recom]推荐单商品信息缺失用法用量详情,dosageCycle: {}, dosageCount:{}, dosageCycleUnit:{}, productName:{}, usages:{} ",
                            item.getDosageCycle(), item.getDosageCount(), item.getDosageCycleUnit(), item.getProductName(), item.getUsage());
                    throw new BusinessException(ExceptionCodes.RECOM_USAGE_UNCOMPLETE_ERROR);
                }
                //if (item.getDrugCycle() == null || StringUtils.isBlank(item.getDrugCycleUnit())) {
                //    LOGGER.warn("[recom]推荐单商品信息缺失用药周期, productName:{}, drugCycle:{}, drugCycleUnit:{} ",
                //            item.getProductName(), item.getDrugCycle(), item.getDrugCycleUnit());
                //    throw new BusinessException(ExceptionCodes.RECOM_USAGE_UNCOMPLETE_ERROR);
                //}

            }
        }

        // 校验患者姓名、年龄
        String patientName = vo.getPatientName();
        if (!LimitValueConfig.validateLimit(LimitValueConfig.PATIENT_NAME, patientName)) {
            LOGGER.warn("患者姓名长度不符合规定,patientName : {}", patientName);
            throw new BusinessException(ExceptionCodes.INVALID_CONTENT_LENGTH);
        }

        if (patientName != null) {
            LOGGER.debug("患者姓名原始输入: patientName:{}", patientName);
            // 将中文标点符号替换为英文标点符号 from V2.9.2
            patientName = NameUtils.replaceChinesePunctuation(patientName);

            if (!patientName.trim().matches(NameUtils.VALIDATE_REGEX_USER_NAME)) {
                LOGGER.warn("患者姓名包含特殊字符: patientName:{}", patientName);
                throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_PATIENT_NAME_INPUT_INVALID);
            }
            vo.setPatientName(patientName);
        }

//
//        Integer patientAge = vo.getPatientAge();
//        LimitConfigVO patientAgeConfig = LimitValueConfig.getLimitConfig(LimitValueConfig.PATIENT_AGE);
        // 1-200岁
//        if (patientAge == null || patientAge < patientAgeConfig.getMin() || patientAge > patientAgeConfig.getMax()) {
//            LOGGER.warn("年龄不合理！ age:{}", patientAge);
//            throw new BusinessException(ExceptionCodes.REMARK_AGE_INPUT_INVALID);
//        }
        // 校验输入的年龄单位，允许的值（空，周，个月，岁）
//        String ageUnit = vo.getPatientAgeUnit();
//        if (StringUtils.isNotBlank(ageUnit) && AgeUnitEunm.val(ageUnit) == null) {
//            LOGGER.warn("年龄单位不合法！ patientAgeUnit:{}", ageUnit);
//            throw new BusinessException(ExceptionCodes.RECOM_AGE_INPUT_INVALID);
//        }

        // 校验推荐类型，如果推荐类型不为普通推荐则求药ID必传
        if (vo.getType().equals(MedicationRecom.RECOM_TYPE_REQUIRE)) {
            if (vo.getRequireId() == null || vo.getRequireId() < 1) {
                LOGGER.warn("[recom]回复求药推荐,求药ID无效, requireId=" + vo.getRequireId());
                throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_REQUIREID_VALID);
            }
        } else {
            vo.setType(MedicationRecom.RECOM_TYPE_MANNUL);
            vo.setRequireId(0L);
        }

    }

    /**
     * 诊断医生检测是否包含非标准诊断
     * @param doctorInfo
     * @param recomParamVO
     * @throws BusinessException
     */
    private void verifyRecomDiagnosis(RecomDoctorBO doctorInfo, RecomParamVO recomParamVO) throws BusinessException {
        if (doctorInfo == null) {
            LOGGER.warn("[recom]医生信息不存在");
            throw new BusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }
        List<String> diagnosis = recomParamVO.getDiagnosis();
        List<DrStandardDiagnosis> drStandardDiagnosisList = drStandardDiagnosisRepository.findByNameIn(diagnosis);
        if (CollectionUtils.isEmpty(drStandardDiagnosisList)) {
            LOGGER.warn("[recom]诊断医生含有非标准诊断");
            throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_EXIST_CUSTOMER);
        }
        if (diagnosis.size() != drStandardDiagnosisList.size()) {
            LOGGER.warn("[recom]诊断医生含有非标准诊断");
            throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_EXIST_CUSTOMER);
        }
    }

    @Override
    public void verifyRecomLimit(Long doctorId, Long inquirerId) throws BusinessException {
        RecomUserInfoBO userInfo = remoter.getUserInfo(doctorId, null, false);
        RecomDoctorBO doctorInfo = userInfo != null ? userInfo.getDoctor() : null;
        verifyRecomLimit(doctorInfo, inquirerId);
    }

    @Override
    public List<String> generateRecomPdf(String hospitalName, Set<Long> recomIdSet) {
        //List<MedicationRecom> recomList = this.medicationRecomDao.getMedicationRecomByIds(recomIdSet);
        //List<DrMedicationRecomInfo> recomInfoList = this.medicationRecomInfoDao.getRecomInfoByRecomIds(recomIdSet);
        //Map<Long, List<MedicationRecomDetail>> details = medicationRecomDetailDao.getRecomDetails(recomIdSet);
        //Map<Long, DrMedicationRecomInfo> recomInfoMap = recomInfoList.stream().collect(Collectors.toMap(DrMedicationRecomInfo::getRecommendId, i -> i));
        //
        List<String> urls = new ArrayList<>();
        //for(MedicationRecom recom : recomList){
        //    DrMedicationRecomInfo info = recomInfoMap.get(recom.getId());
        //    if(!info.getCheckingStatus().equals(DrMedicationRecomInfo.CHECKING_STATUS_PASS)){
        //        LOGGER.info("处方未通过 recomId:{}, recomSn:{}", recom.getId(), recom.getSerialNumber());
        //        continue;
        //    }
        //    List<MedicationRecomDetail> detail = details.get(recom.getId());
        //    List<RecomItemParamVO> skuItems = detail.stream().map(d -> {
        //        RecomItemParamVO items = new RecomItemParamVO();
        //        items.setCommonName(d.getCommonName());
        //        items.setProductName(d.getName());
        //        items.setRecomName(d.getRecomName());
        //        items.setBackup(d.getBakup());
        //        items.setUsage(d.getUsages());
        //        items.setQuantity(d.getQuantity());
        //        items.setQuantityUnit(d.getQuantityUnit());
        //        items.setSalePrice(StringUtil.formatFen2YuanStr(d.getSalePrice()));
        //        return items;
        //    }).collect(Collectors.toList());
        //
        //    String url = this.savePrescriptionFile(hospitalName, recom, info, skuItems);
        //    urls.add(url);
        //}
        return urls;
    }

    /**
     * 推药条件校验
     * RecomServiceImpl.verifyRecomLimit()
     * @Author guoyongxiang
     * @Date 2017年8月1日
     * @since 2.9.0
     * @param doctorInfo
     * @throws BusinessException
     */
    private void verifyRecomLimit(RecomDoctorBO doctorInfo, Long inquirerId) throws BusinessException {
        if (doctorInfo == null) {
            LOGGER.warn("[recom]医生信息不存在");
            throw new BusinessException(ExceptionCodes.DOCTOR_NOT_EXIST);
        }
        // 推药规则限制(0:不限制，1:认证后, 2:推药后)
        Integer recomLimit = ConfigUtil.getInt("recom.limit");
        if (recomLimit >= 1) {
            // 认证状态校验
            if (doctorInfo.getDoctorStatus() != PropertyValueConstants.DOCTOR_AUTH_STATUS_SUCCESS) {
                LOGGER.warn("[recom]推荐单未认证医生已达到试用次数doctorId={}", doctorInfo.getId());
                throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_MEDIC_TRIAL_LIMIT, "请您完成医生认证后再使用此功能！");
            }
        }

        if (recomLimit >= 2) {
            // 认证未通过禁止推药
            if (PropertyValueConstants.DOCTOR_AUTH_STATUS_SUCCESS != doctorInfo.getDoctorStatus()) {
                LOGGER.warn("[recom]需要完成备案才可推药 doctorId:{}", doctorInfo.getId());
                throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_MUST_RECORD, "请您完成医生备案后再使用此功能！");
            }
        }

        List<Long> inquirerIdList = new ArrayList<>();
        inquirerIdList.add(inquirerId);
        // 就诊人小于6岁不能开具处方
//        List<PatientInquirerBO> patientInquirerBOList = patientFeignClient.getPatientInquirer(inquirerIdList);
//        if (CollectionUtil.isNotEmpty(patientInquirerBOList)) {
//            PatientInquirerBO patientInquirerBO = patientInquirerBOList.get(0);
//            if (patientInquirerBO.getAgeYear() < 6) {
//                throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_INQUIRER_AGE_NOT_ENOUGH);
//            }
//        }
    }

    /**
     * 获取诊断字符串文字
     * RecomServiceImpl.getDiagnosisStr()
     * @Author guoyongxiang
     * @Date 2017年8月1日
     * @since 2.9.0
     * @param diagnosisList
     * @return
     */
    private String getDiagnosisStr(List<String> diagnosisList) {
        if (diagnosisList == null || diagnosisList.isEmpty()) {
            return "";
        }
        StringBuilder bd = new StringBuilder();
        for (String name : diagnosisList) {
            bd.append(name).append(",");
        }
        return bd.substring(0, bd.length() - 1);
    }

    /**
     * 设置CRM审核配置本地化设置
     * RecomCreaterServiceImpl.settingCrmConfig()
     * @Author guoyongxiang
     * @Date 2017年8月3日
     * @since 2.9.0
     * @param recomInfo
     * @param recordStatus
     */
    private void settingCrmConfig(DrMedicationRecomInfo recomInfo, Integer recordStatus) {

        // 获取远程CRM审核配置
        CrmRecomSettingVO recomSetting = recomSettingFunc.getSetting();

        // 设置本地化CRM配置默认值
        Integer checkingConfig = DrMedicationRecomInfo.AUDIT_TYPE_AUTO;
        Integer checkingPinStatus = DrMedicationRecomInfo.CHECKING_PIN_STATUS_OFF;
        Integer checkingStatus = DrMedicationRecomInfo.CHECKING_STATUS_DEF;
        Integer pharmacistSignConfig = DrMedicationRecomInfo.PHARMACIST_SIGN_CONFIG_OFF;
        Integer auditStatus;
        //如果处方触发安全用量,则使用用量超量审核开关,否则使用普通审核开关
        auditStatus = recomSetting.getAudit();
        //CRM3.3 增加未备案医生也需要审核需求,所以在此删掉备案条件判断 created by  wanglin 2018-11-05
        //CRM审核开关开启，人工审核
        if (CrmRecomSettingVO.AUDIT_TYPE_MANUAL.equals(auditStatus)) {
            checkingConfig = DrMedicationRecomInfo.AUDIT_TYPE_MANUAL;
            checkingStatus = DrMedicationRecomInfo.CHECKING_STATUS_IN;
        } else if (CrmRecomSettingVO.AUDIT_TYPE_AUTO.equals(auditStatus) && recomSetting.getDelay() > 0) {
            // CRM审核开关关闭且有延时，自动审核
            checkingConfig = DrMedicationRecomInfo.AUDIT_TYPE_AUTO;
            checkingStatus = DrMedicationRecomInfo.CHECKING_STATUS_IN;
        }
        // 设置CRM审核PIN码开关
        if (CrmRecomSettingVO.RECOM_PIN_ON == recomSetting.getPin()) {
            checkingPinStatus = DrMedicationRecomInfo.CHECKING_PIN_STATUS_ON;
        }

        // 药师电子签名开关
        if (CrmRecomSettingVO.RECOM_SIGN_ON == recomSetting.getSign()) {
            pharmacistSignConfig = DrMedicationRecomInfo.PHARMACIST_SIGN_CONFIG_ON;
        }

        recomInfo.setCheckingConfig(checkingConfig);
        recomInfo.setCheckingStatus(checkingStatus);
        recomInfo.setCheckingPinStatus(checkingPinStatus);
        recomInfo.setPharmacistSignConfig(pharmacistSignConfig);
        recomInfo.setCheckingDelay(recomSetting.getDelay());
    }

    /**
     * 推荐用药发送逻辑校验
     * RecomCreaterServiceImpl.verifyRecomConfirmInfo()
     * @Author guoyongxiang
     * @Date 2017年8月4日
     * @since 2.9.0
     * @param recomId
     * @param doctorId
     * @param patientId
     * @param forceRecom
     * @param force
     * @param recomInfo
     * @param userInfo
     * @throws BusinessException
     */
    private void verifyRecomConfirmInfo(Long recomId, Long doctorId, Long patientId, Integer forceRecom, Boolean force,
                                        DrMedicationRecomInfo recomInfo, RecomUserInfoBO userInfo) throws BusinessException {
        if (recomInfo == null) {
            LOGGER.warn("[recom]推荐处方信息不存在recomId:{}, doctorId:{}", recomId, doctorId);
            throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_EXITS);
        }

        if (forceRecom != MedicationRecom.FORCE_RECOM_STATUS_NOT_SEND) {
            LOGGER.warn("[recom]推荐处方单重复发送recomId:{}, doctorId:{}", recomId, doctorId);
            throw new BusinessException(ExceptionCodes.DOCTOR_RECOM_ALREADY_SEND);
        }

        // 客户端强制 发送且医生电子签名状态不为 需要签名待签名，则状态异常
        if (force && recomInfo.getDoctorSignStatus() != DrMedicationRecomInfo.DOCTOR_SIGN_STATUS_UNDO) {
            LOGGER.warn("[recom]推荐处方强制发送状态异常recomId:{}, doctorId:{}, force:{}, doctorSignStatus:{}", recomId, doctorId, force,
                    recomInfo.getDoctorSignStatus());
            throw new BusinessException(ExceptionCodes.ILLEGAL_REQUEST);
        }

        if (userInfo == null || userInfo.getDoctor() == null || userInfo.getPatient() == null) {
            LOGGER.warn("[recom]推荐单对应用户不存在recomId:{}, doctorId:{}, patientId:{}", recomId, doctorId, patientId);
            throw new BusinessException(ExceptionCodes.USER_NOT_EXIST);
        }
    }

    /**
     * 同步处方待审核信息
     *
     * @param recom
     * @param recomInfo
     *
     * @throws BusinessException
     */
    private void syncRecomAudit(MedicationRecom recom, DrMedicationRecomInfo recomInfo) throws BusinessException {
        // 药师预签署

        CrmRecomSettingVO settingVO = recomSettingFunc.getSetting();
        RecomAuditLog auditLog = new RecomAuditLog();
        auditLog.setRecomId(recom.getId());
        auditLog.setSerialNumber(recom.getSerialNumber());
        auditLog.setPreAuditStatus(RecomAuditLog.PRE_AUDIT_STATUS_PENDING);
        // 自动审核(默认通过)
        if (CrmRecomSettingVO.AUDIT_TYPE_AUTO.equals(settingVO.getAudit())) {
            auditLog.setPreAuditStatus(RecomAuditLog.AUDIT_STATUS_PASS);
            auditLog.setStatus(RecomAuditLog.AUDIT_STATUS_PASS);
            auditLog.setChangedAt(new Date());
            auditLog.setPreAuditTime(new Date());

            // 自动药师电子签名
            RecomEsignPharmacist pharmacist = esignAutoForPharmacist(recom.getId());
            auditLog.setPrePharmacistId(pharmacist != null ? pharmacist.getPharmacistId() : 0L);
            auditLog.setPrePharmacistName(pharmacist != null ? pharmacist.getName() : "");
            auditLog.setPharmacistId(pharmacist != null ? pharmacist.getPharmacistId() : 0L);
            auditLog.setPharmacistName(pharmacist != null ? pharmacist.getName() : "");
            auditLog.setChangedBy(pharmacist != null ? pharmacist.getName() : "");

        } else {
            auditLog.setStatus(RecomAuditLog.AUDIT_STATUS_PENDING);
            // 获取可审核药师
            //List<PharmacistVO> pharmacists = pharmacistFeignClient.getAuditPharmacist(1);
            /*if (pharmacists != null && pharmacists.size() > 0) {
                auditLog.setPharmacistId(pharmacists.get(0).getId());
                auditLog.setPharmacistName(pharmacists.get(0).getName());

                // 保存操作日志
                recomAuditOperationLogService.addLog(recom.getId(), "系统", RecomAuditOperationLog.TYPE_DISTRIBUTION,
                        pharmacists.get(0).getName());
                // 给药师发送新处方推送通知
                // 发送审核通知
                sendAuditNotice(auditLog.getPharmacistId(), auditLog.getSerialNumber(), RecomAuditOperationLog.TYPE_DISTRIBUTION);
            } else {
                auditLog.setPharmacistId(0L);
            }*/
            auditLog.setPharmacistId(0L);
        }

        auditLog.setAuditType(settingVO.getAudit());
        auditLog.setDoctorId(recomInfo.getDoctorId());
        auditLog.setDoctorName(recomInfo.getDoctorName());
        auditLog.setPatientId(recomInfo.getPatientId());
        auditLog.setPatientName(recomInfo.getPatientName());
        auditLog.setCreatedAt(new Date());
        auditLog.setCreatedBy("system");
        auditLog.setChangedAt(new Date());
        auditLog.setChangedBy("system");
        recomAuditLogRepository.save(auditLog);

    }

    private void sendAuditNotice(Long pharmacistId, String recomSn, Integer type) {
        String pushUrl = ConfigUtil.getString("push.message.server");
        try {
            switch (type) {
                case RecomAuditOperationLog.TYPE_DISTRIBUTION:
                    LOGGER.info("给药师分配处方，发送分配处方系统通知！");
                    AppPushUtil.sendPhPush(pushUrl, pharmacistId, String.format(PUSH_MSG_ASSIGN, recomSn), AppJumpProtocol.PH_HOME);
                    break;

                case RecomAuditOperationLog.TYPE_REDISTRIBUTION:
                    LOGGER.info("给药师分配处方，发送重新分配系统通知！");
                    AppPushUtil.sendPhPush(pushUrl, pharmacistId, String.format(PUSH_MSG_REASSIGN, recomSn), AppJumpProtocol.PH_HOME);
                    break;

                default:
            }
        } catch (Exception e) {
            LOGGER.error("发送处方提醒PUSH异常 pharmacistId:{}, recomSn:{}", pharmacistId, recomSn);
        }

    }

    public RecomEsignPharmacist esignAutoForPharmacist(Long recomId) {
        RecomEsignPharmacist pharmacist = null;
        // 药师电子签名
        Page<RecomEsignPharmacist> pharmacistUsersPage =
                recomEsignPharmacistRepository.findByStatusAndType(RecomEsignPharmacist.STATUS_NORMAL,
                        RecomEsignPharmacist.TYPE_AUTO_AUDIT_USER, PageRequest.of(0, 10));
        List<RecomEsignPharmacist> pharmacistUsers = pharmacistUsersPage == null ? new ArrayList<>() : pharmacistUsersPage.getContent();
        if (pharmacistUsers != null && !pharmacistUsers.isEmpty()) {
            // 最后10名药师中随机
            int index = (int) (Math.random() * pharmacistUsers.size());
            pharmacist = pharmacistUsers.get(index);
            RecomEsignPharmacist finalPharmacist = pharmacist;
            ThreadPoolUtil.execute(() -> {
                try {
                    recomAuditService.pharmacistSign(recomId, "", finalPharmacist, "");
                } catch (BusinessException e) {
                    LOGGER.warn("自动药师电子签名失败 recomId:{}, msg:{}", recomId, e.getMessage());
                } catch (Exception e) {
                    LOGGER.warn("自动药师电子签名异常 recomId:{}", recomId, e);
                }
            });
        }
        return pharmacist;
    }

    /**
     * 发送推荐消息
     * RecomCreaterServiceImpl.sendRecomMessage()
     * @Author guoyongxiang
     * @Date 2017年8月4日
     * @since 2.9.0
     * @param recom
     * @param recomDetail
     * @param recomInfo
     * @return
     * @throws BusinessException
     */
    private IMSendResultDTO sendRecomMessage(MedicationRecom recom, List<MedicationRecomDetail> recomDetail, DrMedicationRecomInfo recomInfo,
                                             String recordId, Integer consultType) throws BusinessException {

        // 读取生成推荐单时校验的发送状态，设置是否发送消息到患者
        boolean sendToPatient = false;
        if (DrMedicationRecomInfo.CHECKING_STATUS_DEF == recomInfo.getCheckingStatus()) {
            sendToPatient = true;
        }

        RecomSendDTO recomSend = new RecomSendDTO();

        recomSend.setRecommendId(recom.getId());
        recomSend.setRequireId(recom.getRequireId());
        recomSend.setDoctorId(recom.getDoctorId());
        recomSend.setPatientId(recom.getPatientId());
        recomSend.setInquirerId(recom.getInquirerId());
        recomSend.setShowPublicFee(recomInfo.getShowPublicFee());
        recomSend.setConsultFee(recomInfo.getConsultFee());
        recomSend.setVisitFee(recomInfo.getVisitFee());
        recomSend.setRecomType(recom.getType());
        recomSend.setSend(sendToPatient);
        recomSend.setConsultType(consultType);
        List<RecomSendItemDTO> recomSendItem = new ArrayList<>();
        for (MedicationRecomDetail detail : recomDetail) {
            recomSendItem.add(new RecomSendItemDTO(detail.getPid(), detail.getSkuId(), detail.getName(),
                detail.getQuantity(), detail.getUsages(),
                    detail.getBakup()));
        }
        recomSend.setItems(recomSendItem);
        // 查询病历
        MedicalRecordDoctorBO mrdBO = remoter.getDrCaseByRecomId(recom.getId());
        // 判断是否有病历-2.19需求
        if (mrdBO != null) {
            if (!StringUtils.isEmpty(recordId) && recordId.equals(mrdBO.getRecordId())) {
                DoctorsMedicalRecordBO doctorsMedicalRecord = new DoctorsMedicalRecordBO();
                // 构建病历消息
                buildRecordMessage(doctorsMedicalRecord, mrdBO);
                recomSend.setDoctorsMedicalRecord(doctorsMedicalRecord);
            } else {
                LOGGER.warn("病历id为空或者病历对象和病历id不对应,recordId:{},boRecordId:{},recomId:{}", recordId, mrdBO.getRecordId(), recom.getId());
                throw new BusinessException(ExceptionCodes.PARAM_ERROR);
            }
        }
        IMSendResultDTO result = remoter.sendRecomMessage(recomSend);
        // 发送完处方后，通知同步处方数据
        //this.remoter.notifySyncRecom(recom.getDoctorId(), recom.getId());

        if (sendToPatient) {
            if (mrdBO != null) {// 有病历
                // 更新病历状态-2.19需求
                try {
                    remoter.updateRecord(recordId);
                } catch (Exception e) {
                    LOGGER.info("更新病历状态异常,message:{}", e.getMessage());
                    throw new BusinessException(ExceptionCodes.FAILED);
                }
            } else {// 没有病历
                // 异步请求保存诊疗记录 modified by guoping since 2.18 原先同步到搜索服务改为同步到病例服务
                MedicalRecordBO medicalRecordBO = buildSyncMedicalRecordBO(recomInfo, recom);
                remoter.addPrescriptionRecord(medicalRecordBO);
            }
            /*
             * 向患者发送短信提醒
             */
            RecomUserInfoBO userDto = remoter.getUserInfo(recom.getDoctorId(), recom.getPatientId(), false);
            if (recom.getType() == MedicationRecom.RECOM_TYPE_REQUIRE && userDto.getPatient().getPhone() != null) {
                SMSUtil.asyncSendSMS(userDto.getPatient().getPhone(), Template.RECOMMEND_REMIND_TO_PATIENT_TEMPLATE,
                        userDto.getDoctor().getHospitalName(), userDto.getDoctor().getDepartmentName(), userDto.getDoctor().getName());
            }
        }

        if (DrMedicationRecomInfo.CHECKING_STATUS_IN == recomInfo.getCheckingStatus()
                && DrMedicationRecomInfo.AUDIT_TYPE_AUTO == recomInfo.getCheckingConfig()) {
            // 若审核中，且延时，则添加自动审核
            //this.recomAuditHandler.waitAudit(recom.getId(), recomInfo.getCheckingDelay());
        }
        return result;
    }


    @Override
    public MedicalRecordBO buildSyncMedicalRecordBO(DrMedicationRecomInfo recomInfo, MedicationRecom recom) {
        MedicalRecordBO medicalRecordBO = new MedicalRecordBO();
        medicalRecordBO.setDoctorId(recomInfo.getDoctorId());
        medicalRecordBO.setDoctorName(recomInfo.getDoctorName());
        medicalRecordBO.setDepartment(recomInfo.getDepartmentName());
        medicalRecordBO.setHospitalId(recomInfo.getRecordHospitalId());
        medicalRecordBO.setHospitalName(recomInfo.getHospitalName());
        medicalRecordBO.setPatientId(recomInfo.getPatientId());
        medicalRecordBO.setName(recomInfo.getPatientName());
        medicalRecordBO.setGender(recomInfo.getPatientGender());
        medicalRecordBO.setAge(recomInfo.getPatientAge());
        medicalRecordBO.setAgeUnit(recomInfo.getPatientAgeUnit());
        medicalRecordBO.setAgeStr(recomInfo.getPatientAgeStr());
        medicalRecordBO.setCaseType(MedicalRecordBO.CASETYPE_RECOM);
        medicalRecordBO.setRelation(MedicalRecordBO.RELATION_ONESELF);
        medicalRecordBO.setCreateAt(new Date());
        medicalRecordBO.setCreateBy("doctor:" + recomInfo.getDoctorId());
        MedicalRecordRecomBO medicalRecordRecomBO = new MedicalRecordRecomBO();
        medicalRecordRecomBO.setRecommendId(recomInfo.getRecommendId());
        medicalRecordRecomBO.setInvalid(recom.getInvalid());
        medicalRecordBO.setRecordRecom(medicalRecordRecomBO);
        return medicalRecordBO;
    }

    /**
     * 获取医生科室信息，处方相关科室只显示一级科室
     * @param userInfo       医生信息
     * @return
     * @throws BusinessException
     * @Author gongbaoqiang
     * @Date 2018年10月16日
     * @Version 3.4.0
     */
    @Override
    public String getDepartmentName(RecomUserInfoBO userInfo) throws BusinessException {
        String departmentName = "";
        //其他医生显示科室为：一级科室 ——医生端V3.4.0需求变动-2018-10-16
        String parentDepartmentName = userInfo.getDoctor().getDepartmentName();
        if (StringUtils.isNotBlank(parentDepartmentName)) {
            departmentName = parentDepartmentName;
        }
        return departmentName;
    }


    /**
     * 重置复购提醒
     * RecomCreaterServiceImpl.rePurchaseRemindReset()
     * @Author guoyongxiang
     * @Date 2017年8月5日
     * @since 2.9.0
     * @param patientId
     */
    private void rePurchaseRemindReset(Long patientId) {
        try {
            remoter.rePurchaseRemindReset(patientId);
        } catch (BusinessException e) {
            //e.printStackTrace();
            LOGGER.warn("[recom]通知复购提醒失败patientId:{}", patientId, e);
        }
    }

    /**
     * 处方中的患者信息和诊断存入病历中
     * RecomCreaterServiceImpl.recomToDrCaseParams()
     * @Author lichaopi
     * @Date 2018年6月27日
     * @since 1.0.0
     * @param vo
     * @param params
     * @throws BusinessException
     */
    private void recomToDrCaseParams(Long doctorId, RecomParamVO vo, DrCaseParams params) throws BusinessException {
        params.setDoctorId(doctorId);
        params.setPatientId(vo.getPatientId());
        params.setPatientName(vo.getPatientName());
        params.setAge(vo.getPatientAge());
        params.setAgeUnit(vo.getPatientAgeUnit());
        params.setPatientGender(vo.getPatientGender());
        // 转换一下，因为保存推荐时会删除掉
        if (vo.getDiagnosis() != null) {
            List<String> diagnosisList = new ArrayList<>();
            for (String str : vo.getDiagnosis()) {
                diagnosisList.add(str);
            }
            params.setDiagnosisList(diagnosisList);
        }

    }

    /**
     * 构建病历消息内容
     * RecomCreaterServiceImpl.buildRecordMessage()
     * @Author lichaopi
     * @Date 2018年6月28日
     * @since 1.0.0
     * @param messageBO
     * @param bo
     */
    @Override
    public void buildRecordMessage(DoctorsMedicalRecordBO messageBO, MedicalRecordDoctorBO bo) {
        messageBO.setMedicalRecordId(bo.getRecordId());// 病历id
        messageBO.setDoctorId(bo.getDoctorId());
        messageBO.setPatientId(bo.getPatientId());
        messageBO.setDoctorName(bo.getDoctorName());// 医生姓名
        messageBO.setDoctorTitle(bo.getTitle());// 职称
        messageBO.setDoctorHospital(bo.getHospitalName());// 医院姓名
        messageBO.setPatientName(bo.getPatientName());// 患者姓名
        messageBO.setPatientGender(bo.getGenderName());// 性别
        messageBO.setAge(bo.getAge() + bo.getAgeUnit());// 年龄
        messageBO.setMainComplaint(bo.getMainComplaint());// 主诉
        messageBO.setDiagnosis(bo.getDiagnosis());// 诊断
        messageBO.setDoctorsSummary(bo.getPatientOrder());// 医生端的医嘱内容
        messageBO.setPatientsMedicalRecordId(bo.getPtRecordId());// 发送患者的病历id
        messageBO.setDisplayType(bo.getDisplayType());// 病历消息类型
    }

    @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public List<PreSignResultVO> savePrescriptionFile(String hospitalName, MedicationRecom recom,
        DrMedicationRecomInfo info, List<RecomItemParamVO> details, Integer requestFrom) {
        List<PreSignResultVO> eSignSerial = new ArrayList<>();
        String filePath = "";
        try {
            filePath = this.generatePrescriptionFile(hospitalName, recom, info, details);
            this.savePrescriptionFile(recom.getSerialNumber(), filePath, recom.getId(), RecomPrescription.TYPE_UNSIGN);
        } catch (Exception e) {
            LOGGER.error("电子签名-生成处方文件失败 doctorId:{}, patientId:{}, recomId:{}", info.getDoctorId(), info.getPatientId(),
                info.getRecommendId(), e);
        }

        try {
            if (!StringUtils.isBlank(filePath)
                && info.getDoctorSignStatus() == DrMedicationRecomInfo.DOCTOR_SIGN_STATUS_UNDO) {
                LOGGER.info("电子签名-需要签名处方,发起预签署 recomId:{}", recom.getId());
                // 调用电子签名厂商获取签名页链接
                RecomSignParamBO preSignVO = RecomSignParamBO.builder().signUserId(info.getDoctorId())
                    .bizSn(recom.getSerialNumber()).createdAt(recom.getCreatedAt()).patientName(info.getPatientName())
                    .patientGender(info.getPatientGender()).patientAge(info.getPatientAge() + info.getPatientAgeUnit())
                    .pdfPath(STATIC_BASE_PATH + filePath).requestFrom(requestFrom).build();
                PreSignResultBO eSign = esignFeignClient.preSignDr(preSignVO);
                if (eSign != null && StringUtils.isNotBlank(eSign.getUniqueId())) {
                    eSignSerial.add(
                        PreSignResultVO.builder().uniqueId(eSign.getUniqueId()).faceUrl(eSign.getFaceUrl()).build());
                }
            }
        } catch (Exception e) {
            LOGGER.error("电子签名-发起预签署失败 doctorId:{}, patientId:{}, recomId:{}", info.getDoctorId(), info.getPatientId(),
                info.getRecommendId(), e);
        }
        return eSignSerial;
    }

    /**
     * 保存处方文件
     *
     * @param name
     * @param path
     * @param recomId
     * @param type
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void savePrescriptionFile(String name, String path, Long recomId, Integer type) {
        RecomPrescription recomPrescription = new RecomPrescription();
        recomPrescription.setPrescriptionPhotoName(name);
        recomPrescription.setRecommendId(recomId);
        recomPrescription.setPrescriptionPhotoUrl(path);
        recomPrescription.setType(type);
        recomPrescription.setCreatedAt(new Date());
        recommPrescriptionRepository.save(recomPrescription);
    }

    @Override
    public RecomPrescription getByRecomIdAndType(Long recomId, Integer type) {
        return recommPrescriptionRepository.findFirstByRecommendIdAndTypeOrderByIdDesc(recomId, type);
    }

    /**
     * 生成处方文件
     *
     * @param recom
     * @param info
     * @param details
     *
     * @return
     *
     * @throws IOException
     * @throws DocumentException
     */
    private String generatePrescriptionFile(String hospitalName, MedicationRecom recom, DrMedicationRecomInfo info,
        List<RecomItemParamVO> details)
        throws IOException, DocumentException, BusinessException, ReflectiveOperationException {
        LOGGER.info("生成处方文件，hospitalName {}, recom {}, info {}, details {}", hospitalName, JsonMapper.toJson(recom), JsonMapper.toJson(info),
                JsonMapper.toJson(details));
        // 文件名： {医生ID}_{患者ID}_{处方编号}.png
        String fileName = recom.getDoctorId() + "_" + recom.getPatientId() + "_" + recom.getSerialNumber() + ".pdf";
        // 文件路径： {basePath}/{医生Id}/{患者Id}_{处方编号}/
        String filePath =
                PREC_BASE_PATH_PREFIX + "/" + recom.getDoctorId() + "/" + recom.getPatientId() + "_" + recom.getSerialNumber() + "/" + fileName;

        // 组装处方对象
        PrescriptionFileService.PrescriptionInfo prescriptionInfo = new PrescriptionFileService.PrescriptionInfo();
        prescriptionInfo.setHospitalName(hospitalName);
        prescriptionInfo.setDoctorName(info.getDoctorName());
        prescriptionInfo.setDepartmentName(info.getDepartmentName());
        prescriptionInfo.setPharmacistName(info.getPharmacistName());
        prescriptionInfo.setDiagnosis(recom.getDiagnosis());
        prescriptionInfo.setSerialNumber(recom.getSerialNumber());
        prescriptionInfo.setCreatedAt(recom.getCreatedAt());
        prescriptionInfo.setPatientName(info.getPatientName());
        prescriptionInfo.setPatientGender(info.getPatientGender());
        prescriptionInfo.setPatientAge(info.getPatientAge());
        prescriptionInfo.setPatientAgeUnit(info.getPatientAgeUnit());
        prescriptionInfo.setPatientAgeStr(info.getPatientAgeStr());
        //添加就诊人id作为病历号，就诊人电话（儿童没有使用监护人手机号）
        prescriptionInfo.setInquirerId(info.getInquirerId());
        //获取就诊人信息
        if (info.getInquirerId() != null){
            List<PatientInquirerBO> inquirerBOS = patientFeignClient.getPatientInquirer(Lists.newArrayList(info.getInquirerId()));
            if (CollectionUtils.isNotEmpty(inquirerBOS)) {
                PatientInquirerBO inquirerBO = inquirerBOS.get(0);
                prescriptionInfo.setPatientPhone(inquirerBO.getPhone());
            }
        }

        prescriptionInfo.setPharmacistName(info.getPharmacistName());

        List<PrescriptionFileService.PrescriptionSku> skus =
                details.stream()
                        .map(d -> PrescriptionFileService.PrescriptionSku.builder()
                    .name(d.getProductName())
                    .quantityUnit(d.getQuantityUnit())
                                .quantity(d.getQuantity())
                                .useges(d.getUsage())
                                .backup(d.getBackup())
                                .build())
                        .collect(Collectors.toList());
        prescriptionInfo.setSkus(skus);

        prescriptionFileService.generatePDF(STATIC_BASE_PATH + filePath, prescriptionInfo);
        return filePath;
    }

    /**
     * 计算用药周期的天数
     * @param drugCycle
     * @param drugCycleUnit
     * @return
     */
    private double computeDays(Double drugCycle, String drugCycleUnit) {
        if (drugCycle == null || StrUtil.isBlank(drugCycleUnit)) {
            return 0D;
        }
        double days;
        switch (drugCycleUnit) {
            case "小时":
                days = 0.0416666666666667;
                break;
            case "周":
                days = 7;
                break;
            case "月":
                days = 30;
                break;
            default:
                days = 1;
                break;
        }
        return drugCycle * days;
    }

    /**
     * 计算购买盒数
     * 
     * @param drugCycle
     *            用药周期
     * @param dosageCycle
     *            用量周期
     * @param dosageCycleUnit
     *            用量周期单位
     * @param packagSpec
     *            最小规格包装数（如：一盒有多少片 ）
     * @param dosageCount
     *            用药频次 如：每日三次
     * @param eachDosageCount
     *            用量 如每次2片
     * @return 计算的盒数
     */
    private int calculationQuantity(double drugCycle, double dosageCycle, String dosageCycleUnit, int packagSpec,
        double dosageCount, double eachDosageCount) {
        // 算出每日频次
        Double dailyFrequency = MedicationUtil.getDailyFrequency(drugCycle, dosageCycle, dosageCycleUnit);
        // 每日频次 * （用药频次 * 每次用量）/ 最小规格包装数
        double quantity = dailyFrequency * (dosageCount * eachDosageCount) / packagSpec;
        // 向上取整
        Double ceil = Math.ceil(quantity);
        return ceil.intValue();
    }
}
